//希望是一个工具模块当中重写的以下方法
//希望在原型上拓展call/apply/bind方法
const utilsModule = (function (Function) {
  //外面注入的Function作为局部变量

  //ctx -> 执行期上下文
  //ctx 必须是一个引用值对象
  //说明只有函数(对象不行)才能调用myCall方法
  Function.prototype.myCall = function (ctx) {
    //举例：
    // var obj1 = {a:1,b:2};
    // var obj2 = {c:3,c:4};
    // var test = function(){console.log(this)}
    // test.call(); window
    // test.call(obj1); {a:1,b:2}
    // test.call(obj2); {c:3,c:4}

    //情况1：ctx有可能不传 如test.myCall();
    //ctx存在就为ctx
    //ctx不存在就为window

    //情况2：ctx有可能不是一个对象 如原始值
    //将ctx Object(ctx)包装一下转为对象
    ctx = ctx ? Object(ctx) : window;

    //情况3：如何改变this指向？
    //默认情况: test函数内部this指向调用者

    //1.函数内部this指向调用者(test.myCall() -> test(){...})
    //2.传入参数定义的一个函数ctx.originFn指向调用者ctx
    //3.将ctx.orginFn的引用地址与this指向调用者的引用地址改为相同的引用地址
    //4.ctx.originFn和test(){...}都为一样
    //5.如{originFn: f(){}} = test f()
    //6.this指向就能改为第一个传入参数
    ctx.originFn = this;

    /**
     * console.log(ctx);
     * {
     *   a: 1,
     *   b: 2,
     *   originFn: ƒ test()
     * }
     */

    //情况4：如何拿到第二参数开始到结束的所有参数作为调用函数的实参列表?
    var args = [];
    //i = 1 -> 排除第一项
    for (var i = 1; i < arguments.length; i++) {
      //字符串写法为了配合eval方法执行同时加入originFn函数的实参里
      args.push("arguments[" + i + "]");
    }

    // console.log(args);
    //['argument[1]', 'argument[2]']

    // console.log(args.toString());
    //arguments[1],arguments[2]

    // console.log(this, arguments);
    //ƒ test() {...}
    //Arguments(3) [{a: 1, b: 2, originFn: ƒ}, 'rose', 'jack']

    //eval()可以执行字符串程序
    //这里的args会展开平铺到fn的实参中去
    const ret = eval("ctx.originFn(" + args + ")");

    //删除this对象里面保存的originFn函数
    delete ctx.originFn;

    //返回test.myCall(obj1, 'rose', 'jack')的结果
    return ret;
  };
})(Function);

export default utilsModule;
